/***************************************************************************
 *	@file 		fetion_util.cpp
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		utility of clfetion program, contain display string, logging
 *				and other common operations
 *	@modify		none
 ***************************************************************************/

#include "fetion_util.h"
#include "fetion_exception.h"

char inputbuf[INPUTBUFSIZE];
struct termios attr;

/*--------------------------------------------------------------------
 * @brief : constructor of programString
 * @param str_len : length of source string
 * @returns : none
 *-------------------------------------------------------------------*/
programString::programString(int str_len)
{
	try {
		if(str_len > MAX_COLOR_LEN) {
			throw fetionException("source string should be no more than %d bytes.", MAX_COLOR_LEN);
		}

		len = str_len + 4096;
		str = new char[len];
		if(!str) {
			throw fetionException("string memory allocation failed.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "constructor of program string failed : %s", ex.what());
	}
}

/*--------------------------------------------------------------------
 * @brief : copy constructor of programString
 * @param pstr : source programString object to construct programString
 * @returns : none
 *-------------------------------------------------------------------*/
programString::programString(const programString &pstr) : len(pstr.len)
{
	try {
		str = new char[len];
		if(NULL == str) {
			throw fetionException("string memory allocation failed.");
		}
		strcpy(str, pstr.str);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "copy construction of program string failed : %s", ex.what());
	}
}

programString &programString::operator= (programString &rhs)
{
	try {
		des_string();
		len = rhs.len;
		str = new char[len];
		if(NULL == str) {
			throw fetionException("string memory allocation failed.");
		}
		strcpy(str, rhs.str);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "copy equal operator failed : %s", ex.what());
	}
	return *this;
}

programString &programString::operator() (const char *rhs,...)
{
	try {
		des_string();
		int str_len = strlen(rhs);
		if(str_len > MAX_COLOR_LEN) {
			throw fetionException("source string should be no more than %d bytes.", MAX_COLOR_LEN);
		}
		len = str_len + 1000;
		str = new char[len];
		if(!str) {
			throw fetionException("string memory allocation failed.");
		}
		va_list p;
		va_start(p,rhs);
		vsnprintf(str, len, rhs, p);
		va_end(p);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "recreate of string failed : %s", ex.what());
		len = 0;
		str = NULL;
	}
	return *this;
}

/*--------------------------------------------------------------------
 * @brief : give font information to C style string
 * @param colorfront : foreground color
 * @param colorback : background color
 * @param bold : whether bold
 * @param glint : whether glint
 * @param underline : whether has underline
 * @returns : return state
 *-------------------------------------------------------------------*/
int programString::color(int colorfront/*=WHITE*/, int colorback/*=BACK_BLACK*/, bool bold/*=FALSE*/,
		bool glint/*=FALSE*/,bool underline/*=FALSE*/)
{
	static char tmp_buf[MAX_COLOR_LEN];
	int ret = FUNC_SUCC;
	
	do {
		try {
			if(NULL == str) {
				throw fetionException("invalid param,source string is NULL.");
			}
			if(len > MAX_COLOR_LEN) {
				throw fetionException("invalid param,source string should be no more than %d bytes.", MAX_COLOR_LEN);
			}

			if(NULL!=getenv("EVENT_SHOW_COLOR_DISABLE") || 0 == isatty(fileno(stdout)) || 0 == isatty(fileno(stderr))) {
				// if environment variable EVENT_SHOW_COLOR_DISABLE is not NULL
				// or stdout is not terminal
				// or stderr is not terminal
				// return source string directly
				break;
			}

			memset(tmp_buf, 0, sizeof(tmp_buf));
			strncpy(tmp_buf, str, strlen(str)+1);
			memset(str, 0, len);
			
			// bold
			if(true == bold) {
				if(0x00 == str[0]) {
					strncat(str,"\033[",strlen("\033[")+1);
					strncat(str,"1",strlen("1")+1);
				}
				else {
					strncat(str,";1",strlen(";1")+1);
				}
			}
			//underline
			if(true == underline) {
				if(0x00 == str[0]) {
					strncat(str,"\033[",strlen("\033[")+1);
					strncat(str,"4",strlen("4")+1);
				}
				else {
					strncat(str,";4",strlen(";4")+1);
				}
			}
			//glint
			if(true == glint) {
				if(0x00 == str[0]) {
					strncat(str,"\033[",strlen("\033[")+1);
					strncat(str,"5",strlen("5")+1);
				}
				else {
					strncat(str,";5",strlen(";5")+1);
				}
			}

			if(len < strlen(str)) {
				throw fetionException("string buffer is not long enough.");
			}
			//frontground and background color
			if(0x00 == str[0]) {
				snprintf(str+strlen(str),len-strlen(str),"\033[%d;%dm",colorback,colorfront);
			}
			else {
				snprintf(str+strlen(str),len-strlen(str),";%d;%dm",colorback,colorfront);
			}
			strncat(str, tmp_buf, strlen(tmp_buf)+1);
			strncat(str, "\033[0m", strlen("\033[0m")+1);
		}
		catch(std::exception &ex) {
			WRITE_LOG(LOG_WARNING, "cannot set color for string: %s", ex.what());
			ret = FUNC_FAIL;
		}
	}while(false);

	return ret;
}

void programString::position(const int row, const int col)
{
	static char tmp_buf[MAX_COLOR_LEN];
	memset(tmp_buf, 0, sizeof(tmp_buf));
	snprintf(tmp_buf, sizeof(tmp_buf)-1, "\e[%d;%dH", row, col);
	unsigned int tmplen = strlen(tmp_buf), slen = strlen(str);
	if(tmplen+slen > MAX_COLOR_LEN) {
		return;
	}
	memmove(str+strlen(tmp_buf), str, strlen(str));
	memcpy(str, tmp_buf, strlen(tmp_buf));
}
/*--------------------------------------------------------------------
 * @brief : constructor of sysinfoString
 * @param src_str : source string to construct sysinfoString
 * @returns : none
 *-------------------------------------------------------------------*/
sysinfoString::sysinfoString(const char *src_str,...) : programString(strlen(src_str))
{
	try {
		if(NULL == src_str) {
			throw fetionException("source string is NULL.");
		}
		va_list p;
		va_start(p,src_str);
		vsnprintf(str, len, src_str, p);
		va_end(p);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "constructor of sysinfoString failed : %s", ex.what());
		*str = '\0';
	}
}

/*--------------------------------------------------------------------
 * @brief : constructor of messageString
 * @param src_str : source string to construct messageString
 * @returns : none
 *-------------------------------------------------------------------*/
messageString::messageString(const char *src_str,...) : programString(strlen(src_str))
{
	try {
		if(NULL == src_str) {
			throw fetionException("source string is NULL.");
		}
		va_list p;
		va_start(p,src_str);
		vsnprintf(str, len, src_str, p);
		va_end(p);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "constructor of messageString failed : %s", ex.what());
		*str = '\0';
	}
}

/*--------------------------------------------------------------------
 * @brief : constructor of errorString
 * @param src_str : source string to construct errorString
 * @returns : none
 *-------------------------------------------------------------------*/
errorString::errorString(const char *src_str,...) : programString(strlen(src_str))
{
	try {
		if(NULL == src_str) {
			throw fetionException("source string is NULL.");
		}
		va_list p;
		va_start(p,src_str);
		vsnprintf(str, len, src_str, p);
		va_end(p);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "constructor of errorString failed : %s", ex.what());
		*str = '\0';
	}
}

/*--------------------------------------------------------------------
 * @brief : constructor of listenString
 * @param src_str : source string to construct listenString
 * @returns : none
 *-------------------------------------------------------------------*/
listenString::listenString(const char *src_str,...) : programString(strlen(src_str))
{
	try {
		if(NULL == src_str) {
			throw fetionException("source string is NULL.");
		}
		va_list p;
		va_start(p,src_str);
		vsnprintf(str, len, src_str, p);
		va_end(p);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "constructor of listenString failed : %s", ex.what());
		*str = '\0';
	}
}

xmlpp::Node *xmlParser::get_specify_node(xmlpp::Node *pNode, std::string name) const
{
	xmlpp::Node *tmp, *p = pNode;
	xmlpp::Node::NodeList children_list = p->get_children();

	if(p->get_name() == name) {
		return p;
	}
	else {
		for(xmlpp::Node::NodeList::const_iterator iter = children_list.begin();
			iter != children_list.end(); ++iter) {
			if((tmp = get_specify_node(*iter, name)) != NULL) {
				return tmp;
			}
		}
	}
	return NULL;
}

xmlpp::Node *xmlParser::get_specify_node_from_root(std::string name) const
{
	xmlpp::Node *pn = get_document()->get_root_node();
	return get_specify_node(pn, name);
}

std::string xmlParser::xml_get_text_content(const xmlpp::Node *pnode) const
{
	const xmlpp::Element *elem = dynamic_cast<const xmlpp::Element*>(pnode);
	const xmlpp::TextNode *node_text = elem->get_child_text();
	std::string content;
	if(node_text) {
		content = node_text->get_content();
	}
	return content;
}

/*--------------------------------------------------------------------
 * @brief : print string to file (default stdout)
 * @param fd : file handle
 * @param pstr : programString object, contain string to print
 * @returns : file handle for next "<<"
 *-------------------------------------------------------------------*/
FILE* operator<<(FILE *fd, programString &pstr)
{
//	std::streambuf *backup;
	
	try {
		if(fd == NULL) {
			throw fetionException("file handle is NULL.");
		}
		if(fd == stdout) {
			pstr.set_font();
		}
//		backup = std::cin.rdbuf();
//		std::cout<<pstr.str;
//		delline();
		fprintf(fd, "%s", pstr.str);
//		std::cout.rdbuf(backup);
//		std::cout<<std::flush;
		pstr.print_input_buf(fd);
		fflush(fd);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "can't print string, %s", ex.what());
	}
	return fd;
}

/*--------------------------------------------------------------------
 * @brief : constructor of fetionLog
 * @param logpath : log file path
 * @returns : none
 *-------------------------------------------------------------------*/
fetionLog::fetionLog(const char *logpath) : filestream(NULL), use(new size_t(1))
{
	do {
		if(NULL == logpath || '\0' == *logpath) {
			break;
		}
		if(0 != access(logpath, F_OK) || 0 == access(logpath, W_OK)) {
			filestream = fopen(logpath, "w");
		}
		else {
			filestream = NULL;
		}
	} while(false);
}

/*--------------------------------------------------------------------
 * @brief : print log text to log file
 * @param logtype : log type
 * @param log : log text
 * @returns : return state
 *-------------------------------------------------------------------*/
int fetionLog::print_log(logType logtype, const char *log,...) const
{
	int ret = FUNC_SUCC;
	do {
		if(NULL == filestream) {
			ret = FUNC_FAIL;
			break;	// don't write logfile
		}
		if(NULL == log) {
			ret = FUNC_FAIL;
			break;
		}
		char type_str[20] = {0};
		char time_str[32] = {0};
		char fmt[4096] = {0};
		switch (logtype) {
			case LOG_TRACE: 	strcpy(type_str , "TRACING: "); 	break;
			case LOG_WARNING: 	strcpy(type_str , "WARNING: "); 	break;	
			case LOG_FATAL: 	strcpy(type_str , "ERROR:   "); 	break;
			case LOG_DEBUG: 	strcpy(type_str , "DEBUG:   "); 	break;
			default: 			strcpy(type_str , "UNKNOWN: "); 	break;
		}
		va_list ap;
		struct tm *t = get_currenttime();
		strftime(time_str , sizeof(time_str) , "%T" , t );
		va_start(ap, log);
		vsnprintf(fmt, sizeof(fmt), log, ap);
		va_end(ap);

		fprintf(filestream, "[%s]  %-9s %s\n", time_str, type_str, fmt);
		fflush(filestream);
	} while(false);
	
	return ret;
}

std::map<std::string, int> Command::cmdMap;
void Command::setCmdMap()
{
	const std::map<std::string, int>::value_type init_value[] = 
	{
		std::map<std::string, int>::value_type("q", CMD_QUIT_PROGRAM),
		std::map<std::string, int>::value_type("oc", CMD_LISTS_COMMAND_LIST),
		std::map<std::string, int>::value_type("lc", CMD_LISTS_CONTACTS_LIST),
		std::map<std::string, int>::value_type("r", CMD_SEND_TO_RECENT_CONTACT),
		std::map<std::string, int>::value_type("t", CMD_SEND_TO_INDICATED_CONTACT),
		std::map<std::string, int>::value_type("clc", CMD_CLEAR_SCREEN),
		std::map<std::string, int>::value_type("g", CMD_GET_CONTACT_INFORMATION),
		std::map<std::string, int>::value_type("s", CMD_SEND_DIRECT_SMS),
		std::map<std::string, int>::value_type("p", CMD_SEND_TO_PHONE),
		std::map<std::string, int>::value_type("accept", CMD_ACCEPT_ADD_BUDDY_REQUEST),
		std::map<std::string, int>::value_type("refuse", CMD_REFUSE_ADD_BUDDY_REQUEST)
	};
	for(int i = 0; i < 11; i++) {
		cmdMap.insert(init_value[i]);
	}
}

void Command::parse_command()
{
	std::string::size_type pos;
	std::string tmp;
	std::string operation;

	pos = cmdstr.find(" ", 0);
	if(pos == std::string::npos) {
		operation = cmdstr;
	}
	else {
		operation = cmdstr.substr(0, pos);
	}
	
	WRITE_LOG(LOG_TRACE, "user input command : \"%s\".", cmdstr.c_str());
	if(cmdMap.count(operation)) {
		choiceIndex = cmdMap[operation];
	}
	else {
		choiceIndex = atoi(cmdstr.c_str());
	}

	switch (choiceIndex) {
		case CMD_SEND_TO_INDICATED_CONTACT:
			if(cmdstr == "t" || cmdstr == "5") {
				paraString.clear();
			}
			else {
				paraString = cmdstr.substr(pos+1, cmdstr.length()-pos-1);
			}
			paraIndex = 0;
			break;
		case CMD_SEND_TO_PHONE:
			paraString = cmdstr.substr(pos+1, cmdstr.length()-pos-1);
			paraIndex = 1;
			break;
		case CMD_GET_CONTACT_INFORMATION:
		case CMD_SEND_DIRECT_SMS:
			paraString = cmdstr.substr(pos+1, cmdstr.length()-pos-1);
			break;
		case CMD_ACCEPT_ADD_BUDDY_REQUEST:
			tmp = cmdstr.substr(pos+1, cmdstr.length()-pos-1);
			paraIndex = atoi(tmp.c_str());
			addBuddyResult = true;
			break;
		case CMD_REFUSE_ADD_BUDDY_REQUEST:
			tmp = cmdstr.substr(pos+1, cmdstr.length()-pos-1);
			paraIndex = atoi(tmp.c_str());
			addBuddyResult = false;
			break;
		default:
			paraString.clear();
			break;
	}
}

/*--------------------------------------------------------------------
 * @brief : get object of current time struct 
 * @returns : object of current time struct 
 *-------------------------------------------------------------------*/
struct tm* get_currenttime()
{
	time_t t;
	time(&t);
	return localtime(&t);
}

std::string hextostr(const unsigned char *in, int len)
{
	char *res= new char[len*2+1];
	int i = 0;
	while(i < len)
	{
		sprintf(res + i*2 , "%02x" , in[i]);
		i++;
	};
	i = 0;
	int reslength;
	reslength=(int)strlen(res);
	while(i < reslength)
	{
		res[i] = toupper(res[i]);
		i++;
	};
	std::string res_str = std::string(res);
	delete []res;
	return res_str;
}

unsigned char* strtohex(const char *in, int *len)
{
	unsigned char* out = new unsigned char [strlen(in)/2];
	int i = 0 , j = 0 , k = 0 ,length = 0;
	char tmp[3] = { 0 };
	memset(out , 0 , strlen(in) / 2);
	int inlength;
	inlength=(int)strlen(in);
	while(i < inlength)
	{
		tmp[k++] = in[i++];
		tmp[k] = '\0';
		if(k == 2)
		{
			out[j++] = (unsigned char)strtol(tmp , (char**)NULL , 16);
			k = 0;
			length++;
		}
	}
	if(len != NULL )
		*len = length;
	return out;
}

std::string get_sid_from_sipuri(std::string &sipuri)
{
	std::string::size_type pos1, pos2;
	std::string res;

	if((pos1 = sipuri.find(":", 0)) == std::string::npos) {
		throw fetionException("can't find \":\" from sipuri.");
	}
	if((pos2 = sipuri.find("@", 0)) == std::string::npos) {
		throw fetionException("can't find \"@\" from sipuri.");
	}
	res = sipuri.substr(pos1+1, pos2-pos1-1);
	return res;
}

void xml_convert(std::string &in)
{
	std::string::size_type pos;
	if((pos = in.find("?>", 0)) == std::string::npos) {
		return;
	}
	in = in.substr(pos+2, in.length()-pos-2);
}

struct tm convert_date(const std::string &date)
{
	std::string::size_type pos = date.find(",", 0);
	std::string time = date.substr(pos+2, date.length()-pos-2);
	struct tm dstr;

	strptime(time.c_str() , "%d %b %Y %T %Z" , &dstr);

	dstr.tm_hour += 8;
	if(dstr.tm_hour > 23)
		dstr.tm_hour -= 24;
	return dstr;
}

void set_input_attribute()
{
	struct termios temp;

	tcgetattr(0, &attr);
	memcpy(&temp, &attr, sizeof(struct termios));

	temp.c_lflag &= ~ICANON;
//	temp.c_lflag &= ~ECHO;
	temp.c_cc[VTIME] = 255;
	temp.c_cc[VMIN] = 1;
	tcsetattr(0, TCSANOW, &temp);
}

void reset_input_attribute()
{
	tcsetattr(0, TCSANOW, &attr);
}
